﻿#include "businessnetwork.h"

#include <protocol/protocolBuilder.h>
#include <CipherUtils/cipherutils.h>

#include "../Network/network.h"
#include <nlohmann/json.hpp>

#include <IdGenerator/idgenerator.h>
#include "businessmsghandler.h"

#include "businessuser.h"

#include <string>
#include <UserManager/usermanager.h>
#include <Debug/DEBUG.hpp>
#include <ThreadPool/threadpool.h>
#include <protocol/protocolConverter.h>
#include "businessfile.h"

BusinessNetWork::BusinessNetWork()
{
    protoOpt = std::make_shared<ProtocolOption>();
}

void BusinessNetWork::sendMessageToClient(const hv::SocketChannelPtr &channel,
                                          int type,
                                          int subtype,
                                          int64_t id1,
                                          int64_t id2,
                                          int64_t id3,
                                          unsigned char *data,
                                          int length,
                                          bool initClient)
{

    if (!channel->isConnected()) {
        DEBUG_INFO("tcp连接已丢失，取消发送数据");
        return;
    }
    DEBUG_INFO("server send message : " + std::to_string(type) + "|" + std::to_string(subtype) +" | " + std::to_string(length));
    ProtocolBuilder builder;
    builder.setProtocolOption(protoOpt);

    builder.setHeaderField(0, (int64_t)type);
    builder.setHeaderField(1, (int64_t)subtype);
    builder.setHeaderField(2, id1);
    builder.setHeaderField(3, id2);
    builder.setHeaderField(4, id3);

    // lock the func
    std::shared_ptr<std::mutex> sendLock = getChannelLock(channel);
    sendLock->lock();

    // if used to init the client
    if (initClient)
    {
        auto message = builder.buildWithHeaderAndData(data, length);
        channel->write(message->data(), message->size());
        return;
    }

    AppConfig &config = AppConfig::getInstance();
    // get the mode of the server transmission
    int mode = std::stoi(AppConfig::getInstance().globalValue("TransmissionMode"));
    switch (mode)
    {
    case 1:
    {
        // if the message don't need to be full encrypted
        auto message = builder.buildWithHeaderAndData(data, length);
        channel->write(message->data(), message->size());
        break;
    }
    case 2:
    {
        // if the message only need to encrypt the data part
        UserManager &manager = UserManager::getInstance();

        uint64_t targetID = channel2UserID[channel];
        DEBUG_INFO("send encrypted message to " + std::to_string(targetID));
        User &targetUser = manager.getTargetUser(targetID);

        const std::string &AESKey = targetUser["RemoteAESKey"];
        const std::string &AESIV = targetUser["RemoteAESIV"];

        CipherUtils &utils = CipherUtils::getInstance();
        auto encryptedMessage = utils.AES_Encrypt((char *)data, length,
                                                  (unsigned char *)AESKey.data(),
                                                  (unsigned char *)AESIV.data());
        auto message = builder.buildWithHeaderAndData(encryptedMessage.first.get(), encryptedMessage.second);
        channel->write(message->data(), message->size());
        break;
    }
    default:
        throw std::runtime_error("sendMessageToServer error, wrong encrypt mode");
        break;
    }
    return;
}

hv::SocketChannelPtr BusinessNetWork::getChannel(uint64_t userID)
{
    if (userID2Channel.count(userID))
    {
        return userID2Channel[userID];
    }
    return nullptr;
}

uint64_t BusinessNetWork::getUserID(const hv::SocketChannelPtr &channel)
{
    if (channel2UserID.count(channel))
    {
        return channel2UserID[channel];
    }
    return 0;
}

void BusinessNetWork::addSocketChannel(uint64_t userID, const hv::SocketChannelPtr &channel)
{
    userID2Channel[userID] = channel;
    channel2UserID[channel] = userID;
}

void BusinessNetWork::eraseSocketChannel(uint64_t userID)
{
    auto channel = userID2Channel[userID];
    if (channel2UserID.count(channel))
    {
        channel2UserID.erase(channel);
    }
    if (userID2Channel.count(userID))
    {
        userID2Channel.erase(userID);
    }
}

bool BusinessNetWork::checkTargetUserOnline(uint64_t userID)
{
    return userID2Channel.count(userID);
}

void BusinessNetWork::newNetConnection(const hv::SocketChannelPtr &channel)
{
    if (!channelBuffer.count(channel)) {
        // if not exist
        channelBuffer.emplace(channel, std::make_shared<Buffer>());
        channelLock.emplace(channel, std::make_shared<std::mutex>());
        return ;
    }
    auto& one = channelBuffer[channel];
    one->addedSize = 0;
    one->totalSize = 0;
}

void BusinessNetWork::deleteNetConnection(const hv::SocketChannelPtr &channel)
{
    channelBuffer.erase(channel);
    channelLock.erase(channel);

}
std::shared_ptr<Buffer> BusinessNetWork::getChannelBuffer(const hv::SocketChannelPtr &channel)
{
    return channelBuffer[channel];
}

std::shared_ptr<std::mutex> BusinessNetWork::getChannelLock(const hv::SocketChannelPtr &channel)
{
    return channelLock[channel];
}
void BusinessNetWork::initNet()
{
    Network &net = Network::getInstance();
    // set thread max num
    net.setThreadNum(std::thread::hardware_concurrency() / 2);

    // set the protocol setting;

    protoOpt->append(1, GINT, "type of message");
    protoOpt->append(1, GINT, "sub type of message");
    protoOpt->append(8, GINT, "id of A");
    protoOpt->append(8, GINT, "id of B");
    protoOpt->append(8, GINT, "id of request");
    protoOpt->append(2, GSIZE, "size of total protocol");

    int connfd = Network::getInstance().createsocket(23333);
    if (connfd < 0)
    {
        throw std::runtime_error("create socket: 23333 failed");
    }

    // set tcpclient callback
    // when connect changed
    net.onConnection = [](const hv::SocketChannelPtr &channel)
    {
        if (channel->isConnected())
        {
            BusinessNetWork::getInstance().newNetConnection(channel);
            DEBUG_INFO("new connection");
            // connect a new client
            AppConfig &config = AppConfig::getInstance();
            // init the client
            nlohmann::json message;
            IDGenerator generator(std::stoi(config.globalValue("MechineID")));

            message["ECC_public"] = config.globalValue("ECC_public");
            message["TransmissionMode"] = config.globalValue("TransmissionMode");

            std::string messageStr = message.dump();
            // send client setting
            BusinessNetWork::getInstance().sendMessageToClient(channel,
                                                               7, 2, 0, 0, 0,
                                                               (unsigned char *)messageStr.data(),
                                                               messageStr.size(), true);
            DEBUG_INFO("deal with onConnection end");

        }
        else
        {
            // disconnect
            DEBUG_INFO("disconnect");
            uint64_t userID = BusinessNetWork::getInstance().getUserID(channel);
            BusinessFile::getInstance().eraseUserTransfer(userID);
            BusinessUser::getInstance().disconnect(channel);
            BusinessNetWork::getInstance().deleteNetConnection(channel);
        }
    };

    // when receive message
    net.onMessage = [](const hv::SocketChannelPtr &channel, hv::Buffer *buf)
    {
        BusinessNetWork& net = BusinessNetWork::getInstance();
        std::shared_ptr<Buffer> one = net.getChannelBuffer(channel);
        std::string& buffer = one->buffer;
        int64_t& totalSize = one->totalSize;
        int64_t& addedSize = one->addedSize;

        auto bufSize = buf->size();
        memcpy(buffer.data() + addedSize, buf->data(), bufSize);
        // update added size
        addedSize += bufSize;

        ProtocolConverter converter;
        converter.setProtocolOption(BusinessNetWork::getInstance().getProtocolOption());
        ProtocolBuilder builder;
        builder.setProtocolOption(BusinessNetWork::getInstance().getProtocolOption());

        while(addedSize >= totalSize && addedSize != 0) {
            // complete data received

            // 如果存在一个完整的协议
            if (totalSize > 0) {
                std::shared_ptr<Protocol> protocol = builder.buildFromRawData((void*)buffer.data(), totalSize);
                // 执行这个协议，并从缓冲区中删除
                ThreadPool::getInstance().commit([](std::shared_ptr<Protocol> protocol, const hv::SocketChannelPtr &channel){
                    BusinessMsgHandler::getInstance().handleMsg(protocol, channel);}, protocol, channel);

                std::copy(buffer.begin() + totalSize, buffer.begin() + addedSize, buffer.begin());

                addedSize -= totalSize;
            }
            // 如果缓冲区中还有元素，则读取下一个元素的长度

            if (addedSize >= converter.getHeaderSize()) {
                std::shared_ptr<Protocol> protocol = builder.buildFromRawData((void*)buffer.data(), addedSize);
                // 解析协议的长度
                converter.parseData((unsigned char*)protocol->data());
                // update protocol size
                totalSize = std::get<int64_t>(converter.getHeaderField(5));
            } else {
                // 如果缓冲区中的元素长度不足够读取到协议的长度，则不读
                totalSize = 0;
            }
        }

    };

    net.onWriteComplete = [&](const hv::SocketChannelPtr &channel, hv::Buffer *buf)
    {
        std::shared_ptr<std::mutex> sendLock = BusinessNetWork::getInstance().getChannelLock(channel);
        sendLock->unlock();
    };
}

std::shared_ptr<ProtocolOption> BusinessNetWork::getProtocolOption()
{
    return protoOpt;
}
